<?php

/**
 * DocDataProvider implements a data provider based on DocModel.
 *
 * May be used in the following way:
 * <pre>
 * $dataProvider=new DocDataProvider($model, array(
 *     'query'=>array(
 *         'query'=>'hello',
 *         'order'=>array('chrono' => true),
 *         'limit'=>100,
 *         'offset'=>0,
 *     ),
 *     'pagination'=>array(
 *         'pageSize'=>20,
 *     ),
 * ));
 * // $dataProvider->getData() will return a list of DocModel objects
 * </pre>
 *
 * @property mixed $query The query criteria.
 * @property CSort $sort The sorting object. If this is false, it means the sorting is disabled.
 */
class DocDataProvider extends CDataProvider
{
	/**
	 * @var DocModel
	 */
	public $model;

	/**
	 * @var string the name of key attribute for {@link DocModel}. If not set,
	 * it means the primary key of the corresponding database table will be used.
	 */
	public $keyAttribute;
	private $_query = array();

	/**
	 * Constructor.
	 * @param DocModel $model
	 * @param array $config configuration (name=>value) to be applied as the initial property values of this class.
	 */
	public function __construct($model, $config = array())
	{
		$this->model = $model;
		$this->setId(get_class($model));
		if (!isset($config['sort'])) {
			$config['sort'] = array();
		}
		if (is_array($config['sort']) && !isset($config['sort']['attributes'])) {
			$config['sort']['attributes'] = $model->sortAttributes();
		}
		foreach ($config as $key => $value) {
			$this->$key = $value;
		}
	}

	/**
	 * Returns the query criteria.
	 * @return array the query criteria
	 */
	public function getQuery()
	{
		return $this->_query;
	}

	/**
	 * Sets the query
	 * @param mixed $value the query criteria. This can be either a string or an array
	 * representing the query for {@link DocModel::findAll}
	 */
	public function setQuery($value)
	{
		if (is_array($value)) {
			$this->_query = array_merge($this->_query, $value);
		} else {
			$this->_query['query'] = strval($value);
		}
	}

	/**
	 * Fetches the data from the persistent data storage.
	 * @return array list of data items
	 */
	protected function fetchData()
	{
		// apply pagination
		if (($pagination = $this->getPagination()) !== false) {
			$pagination->validateCurrentPage = false;
			$this->_query['offset'] = $pagination->getOffset();
			$this->_query['limit'] = $pagination->getLimit();
		} else {
			unset($this->_query['offset']);
			unset($this->_query['limit']);
		}
		// apply sort
		if (($sort = $this->getSort()) !== false) {
			$this->applySort($sort);
		}
		// perform search
		$data = $this->model->findAll($this->_query);
		// NOTICE: apply total count after findAll
		if ($pagination !== false) {
			$pagination->setItemCount($this->getTotalItemCount());
		}
		return $data;
	}

	/**
	 * Fetches the data item keys from the persistent data storage.
	 * @return array list of data item keys.
	 */
	protected function fetchKeys()
	{
		$keys = array();
		foreach ($this->getData() as $i => $data) {
			$key = $this->keyAttribute === null ? $data->getPrimaryKey() : $data->{$this->keyAttribute};
			$keys[$i] = is_array($key) ? implode(',', $key) : $key;
		}
		return $keys;
	}

	/**
	 * Calculates the total number of data items.
	 * @return integer the total number of data items.
	 */
	protected function calculateTotalItemCount()
	{
		return $this->model->getXS()->search->getLastCount();
	}

	/** 	 
	 * @param CSort $sort
	 */
	protected function applySort($sort)
	{
		$directions = $sort->getDirections();
		if (empty($directions)) {
			unset($this->_query['order']);
		} else {
			$orders = array();
			foreach ($directions as $attribute => $descending) {
				$definition = $sort->resolveAttribute($attribute);
				if ($definition !== false) {
					$orders[$definition] = $descending;
				}
			}
			$this->_query['order'] = $orders;
		}
	}
}
